home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 428_02 / help / hc.c < prev    next >
C/C++ Source or Header  |  1994-03-13  |  9KB  |  361 lines

  1. /*
  2. ** hc.c
  3. **
  4. ** Help-Compiler, Help compiler utility.
  5. ** Compile with any memory model but large data (compact
  6. ** or large) provides more run-time memory.
  7. **
  8. ** Pictor, Version 1.51, Copyright (c) 1992-94 SoftCircuits
  9. ** Redistributed by permission.
  10. */
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <ctype.h>
  15. #include <string.h>
  16.  
  17. #include <pictor.h>
  18. #include <compress.h>
  19.  
  20. #define MAX_LABEL    80
  21. struct item {
  22.     char label[MAX_LABEL + 1];
  23.     long offset;
  24.     unsigned comp_size;
  25.     unsigned uncomp_size;
  26.     struct item *next;
  27. };
  28.  
  29. struct item *first = NULL;
  30. struct item **array;
  31. char signature[] = "PICTORHLP00";
  32. unsigned num_topics,max_comp = 0,max_uncomp = 0;
  33. unsigned comp_size,uncomp_size;
  34. unsigned errors = 0,warnings = 0;
  35. unsigned line = 0;
  36. char *whtspc = " \t\n";
  37. char buffer[BUFSIZ];
  38. char *outfile;
  39.  
  40. #define WARN   0x00
  41. #define ERROR  0x01
  42. #define FATAL  0x02
  43.  
  44. /*
  45. ** displays diagnostic and counts error
  46. */
  47. void error(char *msg,int type)
  48. {
  49.     static char *error_types[] = {
  50.         "Warning",
  51.         "Error",
  52.         "Fatal error",
  53.     };
  54.  
  55.     printf("%s : ",error_types[type]);
  56.     if(line != 0) printf("Line %d : ",line);
  57.     printf("%s\n",msg);
  58.  
  59.     if(type == WARN)
  60.         warnings++;
  61.     else
  62.         errors++;
  63.  
  64. } /* error */
  65.  
  66. /*
  67. ** sets dos errorlevel to number of errors, closes all files and exits
  68. ** this function deletes the temporary file and if errors have occured,
  69. ** deletes the output file
  70. */
  71. void terminate(void)
  72. {
  73.     fcloseall();
  74.  
  75.     if(errors) {
  76.         printf("\nOutput file not created\n");
  77.         unlink(outfile);
  78.     }
  79.     exit(errors);
  80.  
  81. } /* terminate */
  82.  
  83. /*
  84. ** compare routine for qsort()
  85. */
  86. int compare(const void *elem1,const void *elem2)
  87. {
  88.     return(stricmp(*(char **)elem1,*(char **)elem2));
  89.  
  90. } /* compare */
  91.  
  92. /*
  93. ** builds compressed output file from input file
  94. */
  95. void create_output(char *filename,FILE *tmp_stream)
  96. {
  97.     int len;
  98.     unsigned i;
  99.     FILE *stream;
  100.     NODE *root_node;
  101.     struct item *curr;
  102.     void *inbuff,*outbuff;
  103.     long fpos = 0,fpos2 = 0;
  104.  
  105.     /* compute most efficient compression code tree */
  106.     rewind(tmp_stream);
  107.     root_node = gettree(tmp_stream);
  108.     if(root_node == NULL) {
  109.         error("Insufficient memory",FATAL);
  110.         terminate();
  111.     }
  112.  
  113.     /* open output file */
  114.     stream = fopen(filename,"wb");
  115.     if(stream == NULL) {
  116.         error("Unable to create output file",FATAL);
  117.         terminate();
  118.     }
  119.     /* write signature */
  120.     fwrite(signature,sizeof(char),sizeof(signature),stream);
  121.  
  122.     /* write code tree */
  123.     len = writetree(root_node,(BYTE *)buffer);
  124.     putw(len,stream);
  125.     fwrite(buffer,sizeof(char),len,stream);
  126.  
  127.     /* make room for file offset pointer */
  128.     fpos = ftell(stream);
  129.     fwrite(&fpos,sizeof(long),1,stream);
  130.  
  131.     /* allocate compression buffers */
  132.     inbuff = malloc(max_uncomp);
  133.     outbuff = malloc(max_uncomp + 512);
  134.     if(inbuff == NULL || outbuff == NULL) {
  135.         error("Insufficient memory",FATAL);
  136.         terminate();
  137.     }
  138.  
  139.     /* write compressed help text */
  140.     rewind(tmp_stream);
  141.     for(i = 0,curr = first;curr != NULL;i++,curr = curr->next) {
  142.         fread(inbuff,sizeof(char),curr->uncomp_size,tmp_stream);
  143.         curr->comp_size = compress(inbuff,curr->uncomp_size,outbuff,root_node);
  144.         if(curr->comp_size > max_comp)
  145.             max_comp = curr->comp_size;
  146.         curr->offset = ftell(stream);
  147.         putw(curr->comp_size,stream);
  148.         putw(curr->uncomp_size,stream);
  149.         fwrite(outbuff,sizeof(char),curr->comp_size,stream);
  150.     }
  151.  
  152.     fpos2 = ftell(stream);
  153.  
  154.     /* */
  155.     putw(num_topics,stream);
  156.     putw(max_comp,stream);
  157.     putw(max_uncomp,stream);
  158.  
  159.     /* write sorted file offsets for each topics */
  160.     for(i = 0;i < num_topics;i++) {
  161.         fwrite(&(array[i]->offset),sizeof(array[0]->offset),1,stream);
  162.     }
  163.  
  164.     /* */
  165.     inbuff = realloc(inbuff,uncomp_size);
  166.     outbuff = realloc(outbuff,uncomp_size + 512);
  167.     if(inbuff == NULL || outbuff == NULL) {
  168.         error("Insufficient memory",FATAL);
  169.         terminate();
  170.     }
  171.  
  172.     /* */
  173.     fread(inbuff,sizeof(char),uncomp_size,tmp_stream);
  174.  
  175.     /* compress topic labels */
  176.     comp_size = compress(inbuff,uncomp_size,outbuff,root_node);
  177.  
  178.     /* */
  179.     putw(comp_size,stream);
  180.     putw(uncomp_size,stream);
  181.  
  182.     /* write compressed topic labels to output file */
  183.     fwrite(outbuff,sizeof(char),comp_size,stream);
  184.  
  185.     /* */
  186.     fseek(stream,fpos,SEEK_SET);
  187.     fwrite(&fpos2,sizeof(fpos2),1,stream);
  188.  
  189.     fclose(tmp_stream);
  190.     fclose(stream);
  191.  
  192. } /* create_output */
  193.  
  194. /*
  195. ** parses the input file
  196. ** returns a pointer to the temporary file stream
  197. */
  198. FILE *read_input(char *filename)
  199. {
  200.     FILE *stream,*tmp_stream;
  201.     struct item *new,*curr = NULL;
  202.     int in_topic = FALSE;
  203.     char *cptr;
  204.  
  205.     /* open input file */
  206.     if((stream = fopen(filename,"rt")) == NULL) {
  207.         error("Unable to open input file",FATAL);
  208.         terminate();
  209.     }
  210.     /* create temporary file */
  211.     if((tmp_stream = tmpfile()) == NULL) {
  212.         error("Unable to create temporary file",FATAL);
  213.         terminate();
  214.     }
  215.     for(line = 1;fgets(buffer,BUFSIZ,stream);line++) {
  216.         /* find first non-space character */
  217.         cptr = (buffer + strspn(buffer,whtspc));
  218.  
  219.         if(*cptr == '@') {   /* compiler directive */
  220.             cptr = strtok(cptr + 1,whtspc);
  221.             if(cptr == NULL) {
  222.                 error("Compiler directive expected following '@'",ERROR);
  223.             }
  224.             else if(!strcmpi(cptr,"BEGIN")) {   /* new topic */
  225.                 if(!in_topic) {
  226.                     if((new = malloc(sizeof(struct item))) == NULL) {
  227.                         error("Insufficient memory",FATAL);
  228.                         terminate();
  229.                     }
  230.                     if(first == NULL)
  231.                         first = new;
  232.                     else
  233.                         curr->next = new;
  234.                     curr = new;
  235.                     curr->next = NULL;
  236.                     curr->label[0] = '\0';
  237.                     curr->uncomp_size = 0;
  238.  
  239.                     cptr = strtok(NULL,"");
  240.                     if(cptr == NULL || *(cptr += strspn(cptr,whtspc)) == '\0') {
  241.                         error("@BEGIN must be followed by topic label",ERROR);
  242.                     }
  243.                     else {
  244.                         /* strip trailing whitespace */
  245.                         while(isspace(cptr[strlen(cptr) - 1]))
  246.                             cptr[strlen(cptr) - 1] = '\0';
  247.  
  248.                         if(strlen(cptr) > MAX_LABEL) {
  249.                             error("Topic label too long, truncating",WARN);
  250.                             cptr[MAX_LABEL] = '\0';
  251.                         }
  252.                         strcpy(curr->label,cptr);
  253.  
  254.                         /* search for duplicate labels */
  255.                         for(new = first;new->next != NULL;new = new->next) {
  256.                             if(!stricmp(cptr,new->label)) {
  257.                                 error("Topic already defined",ERROR);
  258.                                 break;
  259.                             }
  260.                         }
  261.                     }
  262.                     in_topic = TRUE;
  263.                 }
  264.                 else error("@BEGIN before @END",ERROR);
  265.             }
  266.             else if(!strcmpi(cptr,"END")) {
  267.                 if(in_topic) {
  268.                     if(curr->uncomp_size > max_uncomp)
  269.                         max_uncomp = curr->uncomp_size;
  270.                     num_topics++;
  271.                     in_topic = FALSE;
  272.  
  273.                     if(strtok(NULL,whtspc) != NULL)
  274.                         error("Extra characters ignored",WARN);
  275.                 }
  276.                 else error("@END before @BEGIN",ERROR);
  277.             }
  278.             else error("Unknown compiler directive",ERROR);
  279.         }
  280.         else {
  281.             if(in_topic) {
  282.                 fwrite(buffer,sizeof(char),strlen(buffer),tmp_stream);
  283.                 curr->uncomp_size += strlen(buffer);
  284.             }
  285.             else if(*cptr != '\0' && *cptr != ';')
  286.                 error("Text outside @BEGIN/@END",ERROR);
  287.         }
  288.     }
  289.     if(ferror(stream)) {
  290.         error("Error reading input file",FATAL);
  291.         terminate();
  292.     }
  293.     else if(in_topic) {
  294.         error("Unexpected end-of-file",ERROR);
  295.         if(curr->uncomp_size > max_uncomp)
  296.             max_uncomp = curr->uncomp_size;
  297.         num_topics++;
  298.     }
  299.     fclose(stream);
  300.  
  301.     /* indicate we're no longer processing input file */
  302.     line = 0;
  303.  
  304.     return(tmp_stream);
  305.  
  306. } /* read_input */
  307.  
  308. /*
  309. ** here's main()
  310. */
  311. void main(int argc,char *argv[])
  312. {
  313.     unsigned i,len;
  314.     FILE *tmp_stream;
  315.     struct item *curr;
  316.  
  317.     printf("HC - Help Compiler, Version 1.51,"
  318.         " Copyright (c) 1992-94 SoftCircuits\n");
  319.     printf("Redistributed by permission.\n\n");
  320.  
  321.     if(argc != 3) {
  322.         printf("Usage: HC <input-file> <output-file>\n");
  323.         terminate();
  324.     }
  325.     outfile = argv[2];
  326.  
  327.     /* process input file */
  328.     tmp_stream = read_input(argv[1]);
  329.  
  330.     if(num_topics == 0) {
  331.         error("Input file contains no help topics",FATAL);
  332.         terminate();
  333.     }
  334.  
  335.     /* build array and sort array of pointers to items */
  336.     if((array = malloc(sizeof(struct item *) * num_topics)) == NULL) {
  337.         error("Insufficient memory",FATAL);
  338.         terminate();
  339.     }
  340.     for(i = 0,curr = first;curr != NULL;i++,curr = curr->next) {
  341.         array[i] = curr;
  342.     }
  343.     qsort(array,num_topics,sizeof(struct item *),compare);
  344.  
  345.     /* write so